package com.huateng.springcloud.jvm;

import lombok.extern.slf4j.Slf4j;


import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Object Pool
 * @param <E>
 */
@Slf4j
public class ObjectPool<E> {

    private static final int DEFAULT_MAX_COUNT = 100;

    //正使用集合，已被同步
    private Set<E> activeSet = Collections.synchronizedSet(new HashSet<>());

    //空闲集合，已被同步
    private Set<E> idleSet = Collections.synchronizedSet(new HashSet<>());

    //最大对象数
    private Integer maxCount = DEFAULT_MAX_COUNT;

    //线程等待监视器
    private final Object lock = new Object();

    public ObjectPool(Integer maxCount){
        this.maxCount = maxCount;
    }

    public synchronized <T> E takeObject(Class<E> clazz) throws Exception {
        E obj = null;
        //如果有空闲对象，则从空闲对象中取出
        if (idleSet.size() > 0){
            Iterator<E> iterator = idleSet.iterator();
            obj = iterator.next();
            log.info("Use idle object");
        }

        if (obj != null){
            idleSet.remove(obj);
            activeSet.add(obj);
        } else {
            int size = activeSet.size() + idleSet.size();
            //如果size超出对象池容量，则阻塞当前线程，直到阻塞完毕
            if (size >= maxCount){
                synchronized (lock){
                    log.warn("Pool is full, wait...");
                    lock.wait();
                }
                return takeObject(clazz);
            } else {
                log.info("Create a new object");
                Method method = clazz.getMethod("init");
                obj = (E) method.invoke(clazz);
                activeSet.add(obj);
            }
        }
        log.info("Object account in pool:{}, active:{}, idle:{}",
                (activeSet.size() + idleSet.size()), activeSet.size(), idleSet.size());
        return obj;
    }

    /**
     * 对象使用完毕，归还对象池
     * @param obj
     */
    public synchronized void returnObject(E obj){
        if (obj != null){
            activeSet.remove(obj);
            idleSet.add(obj);
            synchronized (lock){
                log.info("Awake wait thread");
                lock.notify();
            }
        }
    }






}
